package com.jy.rhin.application.infra.assembler;

import com.jy.framework.base.utils.Converter;
import com.jy.rhin.domain.document.content.entity.DocumentContentDO;
import com.jy.rhin.domain.document.filter.DocumentFilterContext;
import com.jy.rhin.domain.document.filter.DocumentFilterContextData;
import com.jy.rhin.domain.document.submission.entity.*;
import com.jy.rhin.sdk.dto.document.*;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author MaYuXuan
 * @date 2022年6月21日 08:45:19
 * @Description 共享文档汇编器
 */
public class DocumentDtoAssembler {
    private DocumentDtoAssembler() {
    }

    /**
     * 批量构建文档内容DO对象
     *
     * @param submissionSetDto
     * @param contentDtoList
     * @return
     */
    public static List<DocumentContentDO> buildContent(DocumentSubmissionSetDto submissionSetDto, List<DocumentContentDto> contentDtoList) {
        if (CollectionUtils.isEmpty(contentDtoList)) {
            return Collections.emptyList();
        }
        List<DocumentEntryDto> entries = submissionSetDto.getEntries();
        Map<String, DocumentEntryDto> entryDtoMap = entries.stream().collect(Collectors.toMap(DocumentEntryDto::getId, entryDto -> entryDto));
        return contentDtoList.stream()
                .map(contentDto -> buildContent(contentDto, entryDtoMap))
                .collect(Collectors.toList());
    }

    /**
     * 保存文档后，需要将入库的文档主键，更新到dto的id字段，再返回
     *
     * @param dto
     * @param documentContentList
     */
    public static void completeContentDto(DocumentProvideAndRegisterDto dto, List<DocumentContentDO> documentContentList) {
        DocumentSubmissionSetDto submissionSetDto = dto.getSubmissionSet();
        List<DocumentContentDto> documentDtoS = dto.getDocuments();
        Map<String, DocumentContentDto> contentDtoMap = documentDtoS.stream().collect(Collectors.toMap(DocumentContentDto::getId, contentDto -> contentDto));
        Map<String, DocumentContentDO> contentDoMap = documentContentList.stream().collect(Collectors.toMap(DocumentContentDO::getUniqueId, content -> content));
        List<DocumentEntryDto> entries = submissionSetDto.getEntries();
        for (DocumentEntryDto entry : entries) {
            String originEntryId = entry.getId();
            DocumentContentDto originDocument = contentDtoMap.get(originEntryId);
            String documentId = String.valueOf(contentDoMap.get(entry.getUniqueId()).getId());
            entry.setId(documentId);
            originDocument.setId(documentId);
        }

    }

    /**
     * 根据入参dto，构建文档内容DO对象
     *
     * @param contentDto
     * @param entryDtoMap
     * @return
     */
    private static DocumentContentDO buildContent(DocumentContentDto contentDto, Map<String, DocumentEntryDto> entryDtoMap) {
        DocumentContentDO contentDO = new DocumentContentDO();
        DocumentEntryDto entryDto = entryDtoMap.get(contentDto.getId());
        contentDO.setContent(contentDto.getContent());
        contentDO.setTitle(entryDto.getTitle());
        contentDO.setUniqueId(entryDto.getUniqueId());
        contentDO.setRepositoryUniqueId(entryDto.getRepositoryUniqueId());
        contentDO.setHash(entryDto.getHash());
        contentDO.setSize(entryDto.getSize());
        return contentDO;
    }

    /**
     * 根据入参dto，构建文档提交集DO对象
     *
     * @param submissionSetDto
     * @return
     */
    public static DocumentSubmissionDO buildSubmission(DocumentSubmissionSetDto submissionSetDto) {
        DocumentSubmissionDO submissionDO = new DocumentSubmissionDO();
        submissionDO.setHomeCommunityId(submissionSetDto.getHomeCommunityId());
        submissionDO.setSourceId(submissionSetDto.getSourceId());
        submissionDO.setUniqueId(submissionSetDto.getUniqueId());
        submissionDO.setSubmissionTime(submissionSetDto.getSubmissionTime());
        submissionDO.setContentTypeCode(buildRhinCodeable(submissionSetDto.getContentTypeCode()));
        submissionDO.setFolders(buildList(submissionSetDto.getFolders(), DocumentDtoAssembler::buildFolder));
        submissionDO.setEntries(buildList(submissionSetDto.getEntries(), DocumentDtoAssembler::buildEntry));
        submissionDO.setAssociations(buildList(submissionSetDto.getAssociations(), DocumentDtoAssembler::buildAssociation));
        submissionDO.setJsonText(submissionSetDto.getJsonText());
        return submissionDO;
    }

    private static DocumentEntryVO buildEntry(DocumentEntryDto dto) {
        DocumentEntryVO entryVO = new DocumentEntryVO();
        entryVO.setId(Converter.toLong(dto.getId()));
        entryVO.setFormatCode(dto.getFormatCode());
        entryVO.setPatientId(DocumentFilterContext.get(DocumentFilterContextData::getPatientMpi));
        PatientInfoDto patientInfo = dto.getPatientInfo();
        if (Objects.nonNull(patientInfo)) {
            entryVO.setHuanZheXm(patientInfo.getHuanZheXm());
            entryVO.setZhengJianHm(patientInfo.getZhengJianHm());
        }
        entryVO.setOrgId(dto.getOrgId());
        entryVO.setOrgName(dto.getOrgName());
        entryVO.setTitle(dto.getTitle());
        entryVO.setServiceStartTime(dto.getServiceStartTime());
        entryVO.setServiceStopTime(dto.getServiceStopTime());
        entryVO.setStatusCode(dto.getStatusCode());
        entryVO.setUniqueId(dto.getUniqueId());
        entryVO.setRepositoryUniqueId(dto.getRepositoryUniqueId());
        entryVO.setHomeCommunityId(dto.getHomeCommunityId());
        entryVO.setClassCode(buildRhinCodeable(dto.getClassCode()));
        entryVO.setConfidentialityCode(buildRhinCoding(dto.getConfidentialityCode()));
        entryVO.setEventCode(buildList(dto.getEventCode(), DocumentDtoAssembler::buildRhinCodeable));
        entryVO.setHealthcareFacilityTypeCode(buildRhinCodeable(dto.getHealthcareFacilityTypeCode()));
        entryVO.setPracticeSettingCode(buildRhinCodeable(dto.getPracticeSettingCode()));
        entryVO.setTypeCode(buildRhinCodeable(dto.getTypeCode()));
        entryVO.setCreationTime(dto.getCreationTime());
        entryVO.setJsonText(dto.getJsonText());
        return entryVO;
    }

    private static DocumentFolderVO buildFolder(DocumentFolderDto folderDto) {
        DocumentFolderVO folderVO = new DocumentFolderVO();
        folderVO.setId(Converter.toLong(folderDto.getId()));
        folderVO.setHomeCommunityId(folderDto.getHomeCommunityId());
        folderVO.setPatientId(DocumentFilterContext.get(DocumentFilterContextData::getPatientMpi));
        folderVO.setStatusCode(folderDto.getStatusCode());
        folderVO.setUniqueId(folderDto.getUniqueId());
        folderVO.setLastUpdateTime(folderDto.getLastUpdateTime());
        folderVO.setTypeCode(buildList(folderDto.getTypeCode(), DocumentDtoAssembler::buildRhinCodeable));
        return folderVO;
    }

    private static DocumentAssociationVO buildAssociation(DocumentAssociationDto dto) {
        DocumentAssociationVO associationVO = new DocumentAssociationVO();
        associationVO.setId(Converter.toLong(dto.getId()));
        associationVO.setStatusCode(dto.getStatusCode());
        associationVO.setTypeCode(dto.getTypeCode());
        associationVO.setSourceId(dto.getSourceId());
        associationVO.setSourceStatus(dto.getSourceStatus());
        associationVO.setTargetId(dto.getTargetId());
        associationVO.setTargetStatus(dto.getTargetStatus());
        return associationVO;
    }

    private static RhinCodeableVO buildRhinCodeable(RhinCodeableDto rhinCodeableDto) {
        RhinCodeableVO rhinCodeableVO = new RhinCodeableVO();
        if (Objects.nonNull(rhinCodeableDto)) {
            rhinCodeableVO.setText(rhinCodeableDto.getText());
            rhinCodeableVO.setCoding(buildList(rhinCodeableDto.getCoding(), DocumentDtoAssembler::buildRhinCoding));
        }
        return rhinCodeableVO;
    }

    private static RhinCodingVO buildRhinCoding(RhinCodingDto rhinCodingDto) {
        RhinCodingVO rhinCodingVO = new RhinCodingVO();
        rhinCodingVO.setCodeValue(rhinCodingDto.getCodeValue());
        rhinCodingVO.setDisplayName(rhinCodingDto.getDisplayName());
        rhinCodingVO.setCodeSystem(rhinCodingDto.getCodeSystem());
        rhinCodingVO.setCodeSystemName(rhinCodingDto.getCodeSystemName());
        rhinCodingVO.setCodeVersion(rhinCodingDto.getCodeVersion());
        return rhinCodingVO;
    }

    private static <T, V> List<V> buildList(List<T> dtoList, Function<T, V> builder) {
        if (CollectionUtils.isEmpty(dtoList)) {
            return Collections.emptyList();
        }
        return dtoList.stream().map(builder).collect(Collectors.toList());
    }

}
